package com.tbit.uqbike.webmanager.handler;

import com.alibaba.fastjson.JSON;
import com.tbit.common.utils.TraceUtil;
import com.tbit.uqbike.enums.SystemCodeEnum;
import com.tbit.uqbike.manager.exception.AuthenticationException;
import com.tbit.uqbike.manager.exception.BaseException;
import com.tbit.uqbike.object.view.JsonResult;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.validation.BindException;
import org.springframework.validation.FieldError;
import org.springframework.web.HttpMediaTypeNotSupportedException;
import org.springframework.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.method.annotation.MethodArgumentTypeMismatchException;
import org.springframework.web.servlet.support.RequestContext;

import javax.servlet.http.HttpServletRequest;
import javax.validation.ConstraintViolation;
import javax.validation.ConstraintViolationException;
import java.util.Objects;
import java.util.Set;

/**
 * @author Kang
 * @date 2017年12月21日下午2:40:48
 * @description：全局异常处理
 */
@ControllerAdvice
@Slf4j
@Component
public class ExceptionProcessor {

    private JsonResult builtResult(String msgId, String message, Integer code, String ext) {
        if (message == null) {
            message = "null";
        }
        if (!StringUtils.isEmpty(ext)) {
            message = String.format("%s\r\n%s", message, ext);
        }
        return JsonResult.fail(msgId, code, message, TraceUtil.getTraceIdByLocal());
    }

    private JsonResult builtResult(String msgId, String message, Integer code) {
        return builtResult(msgId, message, code, null);
    }

    private JsonResult builtResult(SystemCodeEnum sce, HttpServletRequest request, String ext) {
        //把异常信息国际化
        String message = getContextMessage(sce.getMsgId(), request);
        return builtResult(sce.getMsgId(), message, sce.getCode(), ext);
    }

    private JsonResult builtResult(SystemCodeEnum sce, String defaultMessage, HttpServletRequest request, String ext) {
        //把异常信息国际化
//        String message = getContextMessage(sce.getMsgId(), request);
        return builtResult(sce.getMsgId(), defaultMessage, sce.getCode(), ext);
    }

    private String getContextMessage(String message, HttpServletRequest request) {
        try {
            RequestContext requestContext = new RequestContext(request);
            return requestContext.getMessage(message);
        } catch (Exception ee) {
            return message;
        }
    }

    /**
     * 处理基本异常
     *
     * @param e
     * @param request
     * @return
     */
    @ExceptionHandler(value = {BaseException.class})
    @ResponseBody
    public JsonResult handlerBaseException(BaseException e, HttpServletRequest request) {
        //把异常信息国际化
        String message = getContextMessage(e.getMsgId(), request);
        log.warn("基础异常,接口地址:{},异常信息:{}", request.getRequestURI(), e.getMessage());
        return builtResult(e.getMsgId(), message, e.getCode(), e.getExt());
    }

    /**
     * 处理基本异常
     *
     * @param e
     * @param request
     * @return
     */
    @ExceptionHandler(value = {AuthenticationException.class})
    @ResponseBody
    public JsonResult handlerAuthenticationException(AuthenticationException e, HttpServletRequest request) {
        return builtResult(e.getMsgId(), e.getMessage(), e.getCode(), null);
    }


    /**
     * 处理绑定异常
     *
     * @param e
     * @param request
     * @return
     */
    @ExceptionHandler(value = {BindException.class})
    @ResponseBody
    public JsonResult handlerBindException(BindException e, HttpServletRequest request) {
        SystemCodeEnum sce = SystemCodeEnum.VALIDATE_ERROR;
        FieldError fieldError = e.getBindingResult().getFieldError();
        String defaultMessage = fieldError.getDefaultMessage();
        log.warn("绑定异常,接口地址:{},参数:{}", request.getRequestURI(), JSON.toJSONString(request.getParameterMap()));
        if (fieldError.getDefaultMessage().contains("ConversionFailedException")) {
            sce = SystemCodeEnum.CONVERT_ERROR;
        }

        return builtResult(sce, defaultMessage, request, null);
    }

    /**
     * 处理绑定异常(验证注解在实体类属性上)
     *
     * @param e
     * @param request
     * @return
     */
    @ExceptionHandler(value = {MethodArgumentNotValidException.class})
    @ResponseBody
    public JsonResult handlerMethodArgumentNotValidException(MethodArgumentNotValidException e, HttpServletRequest request) {
        SystemCodeEnum sce = SystemCodeEnum.SYSTEM_ERROR;
        String msgId = e.getBindingResult().getFieldError().getDefaultMessage();
        if (com.tbit.uqbike.webmanager.util.StringUtils.isNotNull(msgId)) {
            String message = getContextMessage(msgId, request);
            return builtResult(msgId, message, SystemCodeEnum.VALIDATE_ERROR.getCode());
        }
        return builtResult(sce, request, null);
    }

    /**
     * 处理绑定异常(验证注解在方法参数上)
     *
     * @param e
     * @param request
     * @return
     */
    @ExceptionHandler(value = {ConstraintViolationException.class})
    @ResponseBody
    public JsonResult handlerConstraintViolationException(ConstraintViolationException e, HttpServletRequest request) {
        SystemCodeEnum sce = SystemCodeEnum.SYSTEM_ERROR;
        Set<ConstraintViolation<?>> constraintViolations = e.getConstraintViolations();
        if (!constraintViolations.isEmpty()) {
            String msgId = constraintViolations.iterator().next().getMessage();
            String message = getContextMessage(msgId, request);
            return builtResult(msgId, message, SystemCodeEnum.VALIDATE_ERROR.getCode());
        }
        return builtResult(sce, request, null);
    }


    @ExceptionHandler(value = {Exception.class})
    @ResponseBody
    public JsonResult handlerException(Exception e, HttpServletRequest request) {
        log.error("服务器异常,接口地址:{},异常信息:{}", request.getRequestURI(), e.getMessage(), e);
        return builtResult(SystemCodeEnum.SYSTEM_ERROR, request, null);
    }

    @ExceptionHandler(value = {MethodArgumentTypeMismatchException.class})
    @ResponseBody
    public JsonResult handlerMethodArgumentTypeMismatchException(MethodArgumentTypeMismatchException e, HttpServletRequest request) {
        String errmsg = "参数转换失败，方法：" + Objects.requireNonNull(e.getParameter().getMethod()).getName()
                + ",期望参数类型：" + e.getParameter().getParameterType()
                + ",参数：" + e.getParameter()
                + ",信息：" + e.getMessage();
        String url = request.getRequestURI();
        log.warn("方法参数不匹配异常，接口地址:{},异常信息:{}", url, errmsg);
        return builtResult(SystemCodeEnum.PARAM_CONVER_ERROR, request, String.format("%s=%s", e.getName(), e.getValue()));
    }

    @ExceptionHandler(HttpRequestMethodNotSupportedException.class)
    @ResponseBody
    public JsonResult handleHttpRequestMethodNotSupportedException(HttpRequestMethodNotSupportedException e, HttpServletRequest request) {
        log.warn("不支持的http请求类型异常,接口地址:{}",request.getRequestURI(), e);
        return builtResult(SystemCodeEnum.HTTP_ERROR, request, null);
    }

    @ExceptionHandler(HttpMediaTypeNotSupportedException.class)
    @ResponseBody
    public JsonResult handleHttpMediaTypeNotSupportedException(HttpMediaTypeNotSupportedException e, HttpServletRequest request) {
        return builtResult(SystemCodeEnum.HTTP_CONTENT_TYPE_ERROR, request, null);
    }


}